package br.edu.unifei.mestrado.kl.util;

import java.util.Comparator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import br.edu.unifei.mestrado.commons.graph.NodeWrapper;

public class DArray {

	private Logger logger = LoggerFactory.getLogger(this.getClass());

	private class VertexDComparator implements Comparator<NodeWrapper> {
		@Override
		public int compare(NodeWrapper o1, NodeWrapper o2) {
			int diff = o1.getD() - o2.getD();
			if (diff == 0) {
				return (int) (o1.getId() - o2.getId());
			}
			return diff;
		}
	}

	// UTIL: Armazena os melhores vertices com valores de D de cada partição
	// set | vertice
	private TreeSet<NodeWrapper> set1 = new TreeSet<NodeWrapper>(new VertexDComparator());
	private TreeSet<NodeWrapper> set2 = new TreeSet<NodeWrapper>(new VertexDComparator());

	// TODO: Verificar esse valor novamente
	// 6 elementos obteve o melhor corte
	// foi testado entre os valores: 2,3,4,5,6,7,8,9,10,50, e 100 para 1000 vertices.
	private static int MAX_NUNBER_WITH_GREAT_D = 3;// recomendado pelo artigo KL.

	/**
	 * Remove, set o valor de D, e insere novamente.
	 * 
	 * @param vertex
	 * @param set
	 * @param D
	 */
	public void updateDValueForVertex(NodeWrapper vertex, int set, int D) {
		if (set == Partition.PART_1) {
			set1.remove(vertex);
			vertex.setD(D);
			addVertexToSet(vertex, set1);
		} else if (set == Partition.PART_2) {
			set2.remove(vertex);
			vertex.setD(D);
			addVertexToSet(vertex, set2);
		} else {
			throw new RuntimeException("Partição " + set + " inválida para adição do vertice " + vertex);
		}
	}

	public void addVertex(NodeWrapper vertex, int set) {
		// UTIL: armazena os 10 vertices com maior valor de D
		if (set == Partition.PART_1) {
			addVertexToSet(vertex, set1);
		} else if (set == Partition.PART_2) {
			addVertexToSet(vertex, set2);
		} else {
			throw new RuntimeException("Partição " + set + " inválida para adição do vertice " + vertex);
		}
	}

	private void addVertexToSet(NodeWrapper vertex, SortedSet<NodeWrapper> set) {
		// UTIL: armazena os MAX_NUNBER_WITH_GREAT_D vertices com maior valor de D
		if (set.size() < MAX_NUNBER_WITH_GREAT_D) {
			set.add(vertex);
		} else {
			NodeWrapper lowest = set.first();
			// if (lowest == null) { //não precisa desse if, pq se o size é melhor, então já adiciona direto.
			// set.add(vertex);
			// } else {
			if (vertex.getD() > lowest.getD()) {
				set.remove(lowest);
				set.add(vertex);
			}
			// }
		}
	}

	public void removeVertex(NodeWrapper vertex, int set) {
		if (set == Partition.PART_1) {
			set1.remove(vertex);
		} else if (set == Partition.PART_2) {
			set2.remove(vertex);
		} else {
			throw new RuntimeException("Partição " + set + " inválida para remoção do vertice " + vertex);
		}
	}

	public Set<NodeWrapper> getSet1() {
		return set1;
	}

	public Set<NodeWrapper> getSet2() {
		return set2;
	}

	@Override
	public String toString() {
		return "DArray [size1=" + set1.size() + " size2=" + set2.size() + "]";
	}

	public void printDs(int max) {
		logger.debug("Set1 ordenado: {}", new Object() {
			@Override
			public String toString() {
				StringBuffer b = new StringBuffer();
				int i = 0;
				int max = 20;
				for (NodeWrapper vertice : set1) {
					b.append(vertice.getD()).append(", ");
					i++;
					if (i > max) {
						break;
					}
				}
				return b.toString();
			}
		});
		logger.debug("Set2 ordenado: {}", new Object() {
			@Override
			public String toString() {

				StringBuffer b = new StringBuffer();
				int i = 0;
				int max = 20;
				for (NodeWrapper vertice : set2) {
					b.append(vertice.getD()).append(", ");
					i++;
					if (i > max) {
						break;
					}
				}
				return b.toString();
			}
		});
	}

}
